/*
 * @Author: Clark
 * @Email: haixuanwoTxh@gmail.com
 * @Date: 2023-01-19 11:46:20
 * @LastEditors: Clark
 * @LastEditTime: 2023-01-30 22:01:29
 * @Description: file content
 */

#include "apue.h"
#include <pthread.h>

struct job {
    struct job *j_next;
    struct job *j_prev;
    pthread_t j_id;     // tells which thread handles this job
    // ... more stuff here ...
};

struct queue {
    struct job  *q_head;
    struct job  *q_tail;
    pthread_rwlock_t q_lock;
};

int queue_init(struct queue *qp)
{
    qp->q_head = NULL;
    qp->q_tail = NULL;

    return pthread_rwlock_init(&qp->q_lock, NULL);
}

void job_insert(struct queue *qp, struct job *jp)
{
    pthread_rwlock_wrlock(&qp->q_lock);

    jp->j_next = qp->q_head;
    jp->j_prev = NULL;

    if (qp->q_head != NULL)
    {
        qp->q_head->j_prev = jp;
    }
    else
    {
        qp->q_tail = jp;    // list was empty
    }

    qp->q_head = jp;
    pthread_rwlock_unlock(&qp->q_lock);
}

void job_append(struct queue *qp, struct job *jp)
{
    pthread_rwlock_wrlock(&qp->q_lock);

    jp->j_next = NULL;
    jp->j_prev = qp->q_tail;

    if (qp->q_tail != NULL)
    {
        qp->q_tail->j_next = jp;
    }
    else
    {
        qp->q_head = jp;
    }

    qp->q_tail = jp;
    pthread_rwlock_unlock(&qp->q_lock);
}

void job_remove(struct queue* qp, struct job *jp)
{
    pthread_rwlock_wrlock(&qp->q_lock);
    if (jp == qp->q_head)
    {
        qp->q_head = jp->j_next;
        if (qp->q_tail == jp)
        {
            qp->q_tail = NULL;
        }
        else
        {
            jp->j_next->j_prev = jp->j_prev;
        }
    }
    else if (jp == qp->q_tail)
    {
        qp->q_tail = jp->j_prev;
        jp->j_prev->j_next = jp->j_next;
    }
    else
    {
        jp->j_prev->j_next = jp->j_next;
        jp->j_next->j_prev = jp->j_prev;
    }

    pthread_rwlock_unlock(&qp->q_lock);
}

struct job* job_find(struct queue* qp, pthread_t id)
{
    struct job* jp = NULL;

    if (pthread_rwlock_rdlock(&qp->q_lock) != 0)
        return NULL;

    for (jp = qp->q_head; jp != NULL; jp = jp->j_next)
    {
        if (pthread_equal(jp->j_id, id))
            break;
    }

    pthread_rwlock_unlock(&qp->q_lock);
    return jp;
}
